package tw.com.hitevision.whiteboard.android.widgets;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.os.Build;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import androidx.core.content.ContextCompat;
import com.hht.wbjni.WbJniCall;
import tw.com.hitevision.whiteboard.android.Device;
import tw.com.hitevision.whiteboard.android.Props;
import tw.com.hitevision.whiteboard.android.R;
import tw.com.hitevision.whiteboard.android.Whiteboard;
import tw.com.hitevision.whiteboard.android.display.DisplayDpiHelper;
import tw.com.hitevision.whiteboard.android.fastdraw.FastDraw;
import tw.com.hitevision.whiteboard.android.render.Line;
import tw.com.hitevision.whiteboard.android.util.BroadcastHelper;
import tw.com.hitevision.whiteboard.android.util.InputManager;
import tw.com.hitevision.whiteboard.android.util.SystemPropertiesUtils;
import tw.com.hitevision.whiteboard.android.util.TouchUtils;


public class FrontSurfaceView extends SurfaceView {
    private static final String TAG = FrontSurfaceView.class.getSimpleName();
    private final float PARTIAL_ERASER_PALM_RADIUS = this.getResources().getDimensionPixelSize(R.dimen.oem_eraser_circle_palm_diameter)/2;
    private final float PARTIAL_ERASER_NORMAL_RADIUS =  this.getResources().getDimensionPixelSize(R.dimen.oem_eraser_circle_default_diameter)/2;
    private final float OBEJECT_ERASER_RADIUS =  this.getResources().getDimensionPixelSize(R.dimen.eraser_object_default_thickness)/2;
    private final float ERASER_AREA_DASHLINE_WIDTH = this.getResources().getDimensionPixelSize(R.dimen.oem_eraser_circle_default_stroke_width);
    private final float TOUCH_EVENT_WAIT_DELAY = 15;
    private Bitmap bitmap;
    private float eraserRadius;
    private Paint partialEraserPaint;
    private Paint objectEraserPaint;
    private Paint demoPaint;
    private Paint eraserAreaPaint;
    private Path eraserPath;
    private Path eraserAreaPath;
    private Context context;
    private OnSurfaceViewCallBack surfaceViewCallBack;
    private SurfaceHolder surfaceHolder = this.getHolder();
    private boolean doingEraser = false;
    private boolean isCanvasLocking = false;
    private Canvas canvas = null;
    private ExecutorService threadPool = Executors.newSingleThreadExecutor();

    private Line eraserLine;
    private FastDraw lineFastDraw = null;

    Device device;

    private Canvas backgroundCanvas = new Canvas();
    private boolean isFirstDown = true;
    private boolean isMultiPointDown = false;

    public FrontSurfaceView(Context context) {
        super(context);
        init(context);
    }

    public FrontSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public FrontSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public FrontSurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    public void setFastDraw(FastDraw fastDraw){
        lineFastDraw = fastDraw;
    }

    private void init(Context context) {
        this.context = context;
        eraserAreaPath = new Path();
        initPaint();

        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (doingEraser) {
                    return true;
                }

                final int index = event.getActionIndex();
                final int pointerId = event.getPointerId(index);
                device = TouchUtils.getEventDevice(context, pointerId, event);
                final Whiteboard.Input input = InputManager.getInstance().getInput(device);


                if ((input == Whiteboard.Input.OBJECT_ERASER || input == Whiteboard.Input.PARTIAL_ERASER)) {
                    switch (event.getActionMasked()) {
                        case MotionEvent.ACTION_DOWN: {
                            BroadcastHelper.sendScreenshotStart(context);//橡皮擦DOWN时，隐藏悬浮窗
                            long now = System.currentTimeMillis();
                            Log.d(TAG, "onTouchdown isfirst: " + isFirstDown);
                            readyToEraser(input);
                            transEventToEraserPoint(event);
                            drawEraser(event.getX(), event.getY(), input, true, true);
                            while (System.currentTimeMillis() - now < TOUCH_EVENT_WAIT_DELAY) {
                                //wait for min limit time
                            }
                            break;
                        }
                        case MotionEvent.ACTION_MOVE: {
                            if (eraserPath == null) {
                                break;
                            }
                            long now = System.currentTimeMillis();
                            isFirstDown = false;
                            transEventToEraserPoint(event);
                            drawEraser(event.getX(), event.getY(), input, true, true);
                            while (System.currentTimeMillis() - now < TOUCH_EVENT_WAIT_DELAY) {
                                //wait for min limit time
                            }
                            break;
                        }
                        case MotionEvent.ACTION_POINTER_UP:
                            if (event.getPointerId(event.getActionIndex()) == 0) {
                                isMultiPointDown = true;
                                WbJniCall.fbDrawScreenRect2(bitmap, event.getX() - eraserRadius - 5, event.getY() - eraserRadius - 5, eraserRadius * 2 + 10, eraserRadius * 2 + 10,
                                        (float) (getResources().getDisplayMetrics().density * 46.78), isFirstDown, false);
                            }
                            BroadcastHelper.sendScreenshotEnd(context);//橡皮擦UP时，显示悬浮窗
                            isFirstDown = true;
                            if (!(device == Device.PALM) || eraserPath == null) {
                                break;
                            }
                        case MotionEvent.ACTION_CANCEL:
                            isFirstDown = true;
                        case MotionEvent.ACTION_UP: {
                            BroadcastHelper.sendScreenshotEnd(context);//橡皮擦UP时，显示悬浮窗
                            if (eraserPath == null) {
                                break;
                            }
                            isFirstDown = true;
                            transEventToEraserPoint(event);
                            isMultiPointDown = false;
                            drawEraser(event.getX(), event.getY(), input, false, false);
                            Log.d(TAG, "onTouch: " + "eraser UP");
                            if (eraserLine != null) {
                                threadPool.execute(new Runnable() {
                                    @Override
                                    public void run() {
                                        eraserLine.updateAll();
                                        surfaceViewCallBack.onEraserDrawOver(input);
                                    }
                                });
                                doingEraser = true;
                            }
                            break;
                        }
                        default: {
                            BroadcastHelper.sendScreenshotEnd(context);//橡皮擦UP时，显示悬浮窗
                        }
                    }
                    return true;
                }
                return false;
            }
        });
    }

    private void readyToEraser(Whiteboard.Input input) {
        eraserPath = new Path();
        if (input == Whiteboard.Input.PARTIAL_ERASER) {
            if (device == Device.PALM) {
                eraserRadius = PARTIAL_ERASER_PALM_RADIUS;
            } else {
                eraserRadius = PARTIAL_ERASER_NORMAL_RADIUS;
            }
            partialEraserPaint.setStrokeWidth(eraserRadius * 2f);
            demoPaint = partialEraserPaint;
        } else {
            eraserRadius = OBEJECT_ERASER_RADIUS;
            demoPaint = objectEraserPaint;
        }
        surfaceHolder.setFormat(PixelFormat.RGB_565);
        surfaceViewCallBack.onEraserReadyToDraw(input);
        backgroundCanvas.setBitmap(bitmap);
    }

    private void transEventToEraserPoint(MotionEvent event) {
        final int historySize = event.getHistorySize();
        for (int i = 0; i < historySize; i++) {
            addEraserPoint(event.getHistoricalX(0, i), event.getHistoricalY(0, i));
        }
        addEraserPoint(event.getX(), event.getY());
    }

    public void setBackgroundBitmap(Bitmap backgroundBitmap) {
        if (backgroundBitmap != null) {
            bitmap = backgroundBitmap;
        }
    }

    public void setEraserBitmap(Bitmap paintBitmap) {
        if (paintBitmap != null) {
            BitmapShader bitmapShader = new BitmapShader(paintBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
            partialEraserPaint.setShader(bitmapShader);
        }
    }

    private void addEraserPoint(float x, float y) {
        if (isMultiPointDown) {
            return;
        }
        if (eraserLine == null) {
            eraserPath.moveTo(x, y);
            eraserLine = new Line(context, x, y, eraserRadius * 2f);
        } else {
            eraserPath.lineTo(x, y);
            eraserLine.add(x, y);
        }
    }


    private void drawEraser(final float x, final float y, Whiteboard.Input input, boolean onDrawFast, boolean drawCircle) {
        if (input == Whiteboard.Input.PARTIAL_ERASER) {
            if (isMultiPointDown) {
                return;
            }

            if (eraserPath != null) {
                backgroundCanvas.drawPath(eraserPath, demoPaint);
            } else {
                return;
            }

            if (Props.getFlavorChipset().equals(Props.Flavor.Chipset.Optoma)) {
                if(lineFastDraw==null)
                    return;

                float tmpRadius = eraserRadius;
                if(device != Device.PALM)
                    tmpRadius *= 3;
                lineFastDraw.drawBitmap(bitmap, x - tmpRadius - 10, y - tmpRadius - 10, tmpRadius * 2 + 20, tmpRadius * 2 + 20,
                        (float) (getResources().getDisplayMetrics().density * 46.78), isFirstDown, drawCircle, partialEraserPaint);
//                lineFastDraw.drawCircle(x, y, eraserRadius, eraserAreaPaint);

            } else {
                if (!drawCircle) {
                    WbJniCall.fbDrawScreenRect2(bitmap, x - eraserRadius - 5, y - eraserRadius - 5, eraserRadius * 2 + 10, eraserRadius * 2 + 10,
                            (float) (getResources().getDisplayMetrics().density * 46.78), isFirstDown, drawCircle);
                } else {
                    WbJniCall.fbDrawScreenRect2(bitmap, x - eraserRadius, y - eraserRadius, eraserRadius * 2, eraserRadius * 2,
                            (float) (getResources().getDisplayMetrics().density * 46.78), isFirstDown, drawCircle);
                }
            }

            if (onDrawFast) {
                return;
            }
        }

        if (isCanvasLocking) {
            return;
        }
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                isCanvasLocking = true;
                try {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//                        canvas = surfaceHolder.lockCanvas();
                        //it will cause crash while switching surface format
                        canvas = surfaceHolder.lockHardwareCanvas();
                    } else {
                        canvas = surfaceHolder.lockCanvas();
                    }
                    if (canvas != null) {
                        if (input == Whiteboard.Input.OBJECT_ERASER) {
                            if (eraserPath != null) {
                                backgroundCanvas.drawPath(eraserPath, demoPaint);
                            }
                        }
                        if (bitmap != null && !bitmap.isRecycled() && !SystemPropertiesUtils.getStringValue(SystemPropertiesUtils.KEY_FRAME_BUFFER_LOCK, FastDraw.LOCK_FRAME_BUFFER_OFF).equals(FastDraw.LOCK_FRAME_BUFFER_ON)) {
                            canvas.drawBitmap(bitmap, 0, 0, null);
                        }
                        if (input == Whiteboard.Input.PARTIAL_ERASER) {
                            /*8386的板擦加速会导致板擦周围的圆圈跟不上手指移动的速度，故暂且去掉圆圈,由加速库实现*/
//                            drawEraserArea(canvas, x, y);
                        }
                    } else {
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                } finally {
                    if (canvas != null) {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                        canvas = null;
                    }
                    isCanvasLocking = false;
                }
            }
        });
    }

    public void resetEraser() {
        clearSurface();
        if (eraserPath != null) {
            eraserPath.reset();
            eraserPath = null;
        }
        if (eraserLine != null) {
            eraserLine.clear();
            eraserLine = null;
        }
        doingEraser = false;
    }

    public void clearSurface() {
        while (isCanvasLocking) {
            //wait
        }
        surfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
        threadPool.execute(new Runnable() {
            @Override
            public void run() {

                isCanvasLocking = true;
                try {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//                        canvas = surfaceHolder.lockCanvas();
//                        it will cause crash while switching surface format
                        canvas = surfaceHolder.lockHardwareCanvas();
                    } else {
                        canvas = surfaceHolder.lockCanvas();
                    }
                    if (canvas != null) {
                        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                    } else {
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                } finally {
                    if (canvas != null) {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                    isCanvasLocking = false;
                }
            }
        });
    }

    public void registerCallBack(OnSurfaceViewCallBack surfaceViewCallBack){
        this.surfaceViewCallBack = surfaceViewCallBack;
    }

    public interface OnSurfaceViewCallBack{
        void onEraserReadyToDraw(Whiteboard.Input input);
        void onEraserDrawing(float x, float y);
        void onEraserDrawOver(Whiteboard.Input input);
    }

    private void drawEraserArea(Canvas canvas,float x,float y){
        eraserAreaPath.reset();
        eraserAreaPath.addCircle(x, y, eraserRadius-ERASER_AREA_DASHLINE_WIDTH,Path.Direction.CCW);
        canvas.drawPath(eraserAreaPath,eraserAreaPaint);
    }

    private void initPaint(){
        float factor = DisplayDpiHelper.getInstance().getFake4kFactor();
        partialEraserPaint = new Paint();
        partialEraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
        partialEraserPaint.setColor(0xFFFFFFFF);
        partialEraserPaint.setAntiAlias(true);
        partialEraserPaint.setStyle(Paint.Style.STROKE);
        partialEraserPaint.setStrokeCap(Paint.Cap.ROUND);
        partialEraserPaint.setStrokeJoin(Paint.Join.ROUND);

        objectEraserPaint = new Paint();
        objectEraserPaint.setColor(ContextCompat.getColor(context, R.color.eraserTrack));
        objectEraserPaint.setStyle(Paint.Style.STROKE);
        objectEraserPaint.setStrokeWidth(6f);
        objectEraserPaint.setPathEffect(new DashPathEffect(new float[]{27f * factor,18f * factor},-0f * factor));
        partialEraserPaint.setAntiAlias(true);

        eraserAreaPaint = new Paint();
        eraserAreaPaint.setColor(context.getResources().getColor(R.color.eraser_oem_dashline_color));
        eraserAreaPaint.setStyle(Paint.Style.STROKE);
        eraserAreaPaint.setStrokeWidth(context.getResources().getDimensionPixelSize(R.dimen.oem_eraser_circle_default_stroke_width));
        eraserAreaPaint.setPathEffect(new DashPathEffect(new float[] {4f * factor, 4f * factor}, -1f * factor));
        eraserAreaPaint.setAntiAlias(true);

        demoPaint = new Paint();
        demoPaint.setStrokeWidth(5);
        demoPaint.setColor(Color.RED);
        demoPaint.setStyle(Paint.Style.STROKE);
        demoPaint.setStrokeCap(Paint.Cap.ROUND);
        demoPaint.setStrokeJoin(Paint.Join.ROUND);
    }

    public Line getEraserLine(){
        return eraserLine;
    }

}
